﻿using Autodesk.Revit.DB;
using RevitProject.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RevitProject.SpaceManager.Core
{
    /// <summary>
    /// Dijkstra最短路径算法实现类
    /// 用于计算空间之间的最短路径
    /// </summary>
    internal class DijkstraCore
    {
        /// <summary>
        /// 获取从起点到终点的最短路径
        /// </summary>
        /// <param name="routeNodeDatas">所有节点数据</param>
        /// <param name="sNodeName">起点节点名称</param>
        /// <param name="eNodeName">终点节点名称</param>
        /// <returns>最短路径上的节点名称列表</returns>
        public List<string> GetRoute(List<RouteNodeData> routeNodeDatas, string sNodeName, string eNodeName)
        {
            // 计算起点到所有节点的最短路径
            var dicPath = Dijkstra(routeNodeDatas, sNodeName);
            // 获取从起点到终点的具体路径
            return GetShortestPath(dicPath, eNodeName);
        }

        /// <summary>
        /// Dijkstra算法实现
        /// 计算从起点到所有节点的最短路径
        /// </summary>
        /// <param name="routeNodeDatas">所有节点数据</param>
        /// <param name="sNodeName">起点节点名称</param>
        /// <returns>每个节点的前驱节点字典</returns>
        private Dictionary<string, string> Dijkstra(List<RouteNodeData> routeNodeDatas, string sNodeName)
        {
            // 优先队列，存储（当前距离，当前节点）
            List<Tuple<double, string>> priority_queue = new List<Tuple<double, string>>();
            priority_queue.Add(new Tuple<double, string>(0, sNodeName));

            // 记录已访问的节点和最短距离
            Dictionary<string, double> dicDistances = new Dictionary<string, double>();
            routeNodeDatas.ForEach(p => dicDistances.Add(p.NodeName, double.MaxValue));
            dicDistances[sNodeName] = 0;

            // 记录路径，存储每个节点的前驱节点
            Dictionary<string, string> paths = new Dictionary<string, string>();
            routeNodeDatas.ForEach(p => paths.Add(p.NodeName, ""));          

            while (true)
            {
                if (priority_queue.Count == 0)
                    break;

                // 获取当前距离最短的节点
                var curNode = priority_queue.First();
                double curDistance = curNode.Item1;
                string curNodeName = curNode.Item2;
                priority_queue.RemoveAt(0);

                // 如果当前距离大于已记录的最短距离，跳过
                if (curDistance > dicDistances[curNodeName])
                    continue;

                // 获取当前节点的所有邻接节点
                var neighborNodes = routeNodeDatas.Find(p => p.NodeName == curNodeName);
                if (neighborNodes == null || neighborNodes.NeighborNodes.IsNullOrEmptyExt())
                    break;

                // 遍历所有邻接节点
                foreach (var neighborNode in neighborNodes.NeighborNodes)
                {
                    string neighbor_room = neighborNode.NodeName;
                    double neighbor_distance = neighborNode.Distance;
                    double total_distance = curDistance + neighbor_distance;

                    // 如果找到更短的路径，更新距离和路径
                    if (total_distance < dicDistances[neighbor_room])
                    {
                        dicDistances[neighbor_room] = total_distance;
                        paths[neighbor_room] = curNodeName;
                        priority_queue.Add(new Tuple<double, string>(total_distance, neighbor_room));
                    }
                }
            }

            return paths;
        }

        /// <summary>
        /// 根据前驱节点字典获取从起点到终点的具体路径
        /// </summary>
        /// <param name="paths">前驱节点字典</param>
        /// <param name="eNodeName">终点节点名称</param>
        /// <returns>路径上的节点名称列表</returns>
        private List<string> GetShortestPath(Dictionary<string, string> paths, string eNodeName)
        {
            List<string> route = new List<string>();

            // 从终点回溯到起点
            string curNode = eNodeName;
            while (curNode.IsNotNullEmptyExt())
            {
                // 防止循环
                if (route.Contains(curNode))
                    break;

                route.Add(curNode);
                curNode = paths[curNode];
            }
            route.Reverse(); // 反转列表，使路径从起点到终点

            return route;
        }
    }

    /// <summary>
    /// 路径节点数据类
    /// 包含节点的基本信息和邻接关系
    /// </summary>
    public class RouteNodeData
    {
        /// <summary>
        /// 节点名称
        /// 用于标识节点
        /// </summary>
        internal string NodeName { get; set; } = "";

        /// <summary>
        /// 节点所在标高
        /// 用于确定节点在建筑中的位置
        /// </summary>
        internal Level NodeLevel { get; set; }

        /// <summary>
        /// 节点坐标
        /// 用于计算节点之间的距离
        /// </summary>
        internal XYZ NodeCoordinate { get; set; }

        /// <summary>
        /// 节点类型
        /// 区分房间、管井、走廊等不同类型
        /// </summary>
        internal NodeType NodeType { get; set; }

        /// <summary>
        /// 相邻节点列表
        /// 存储与该节点相连的所有节点及其距离
        /// </summary>
        internal List<NeighborNodeData> NeighborNodes { get; set; } = new List<NeighborNodeData>();
    }

    /// <summary>
    /// 邻接节点数据类
    /// 存储节点之间的连接关系
    /// </summary>
    internal class NeighborNodeData
    {
        /// <summary>
        /// 邻接节点名称
        /// </summary>
        internal string NodeName { get; set; }       

        /// <summary>
        /// 到邻接节点的距离
        /// </summary>
        internal double Distance { get; set; }
    }

    /// <summary>
    /// 节点类型枚举
    /// 定义不同类型的空间节点
    /// </summary>
    internal enum NodeType
    {
        /// <summary>
        /// 房间类型节点
        /// </summary>
        Room,

        /// <summary>
        /// 管井类型节点
        /// </summary>
        Well,

        /// <summary>
        /// 走廊类型节点
        /// </summary>
        Corridor
    }
}
